#import "ALTemplateLibraryPopoverController.h"
#import "SMRPopoverWindow.h"
#import "ALModelHeaders.h"
#import "NSManagedObjectContext+SMRAdditions.h"
#import "ALTemplateEditorElementColumnEntry.h"
#import "ALMainWindowController.h"


#pragma mark Constants

static const CGFloat ALTemplateLibraryTableCellVerticalPadding = 8.0f;
static const CGFloat ALDetailToolbarTemplateEditorVerticalPadding = 10.0f;
static const CGFloat ALDetailToolbarTemplateEditorHorizontalPadding = 0.0f;
static const CGFloat ALDetailToolbarTemplateEditorGroupPadding = 25.0f;
static const NSSize ALDetailToolbarTemplateLibraryPopoverMinSize = {300, 200};
static const NSSize ALDetailToolbarTemplateLibraryPopoverDefaultSize = {400, 500};
static NSString* const ALDetailToolbarTemplateLibraryPreferredContentSize = @"ALDetailToolbarTemplateLibraryPreferredContentSize";


#pragma mark -
@interface ALTemplateLibraryPopoverController (ALTemplateLibraryPopoverControllerPrivateMethods)
-(void)tileTemplateEditorView_;
-(NSSize)defaultContentSize_;
-(void)setDefaultContentSize_:(NSSize)contentSize;
-(void)updateTemplateEditorKeyViewLoop_;
@end


#pragma mark -
@implementation ALTemplateLibraryPopoverController

@synthesize templateEditorElementsColumnView;
@synthesize templateEditorStandardFieldsView;
@synthesize templateEditorDocumentView;
@synthesize entryTemplatesArrayController;
@synthesize templateLibraryTableView;
@synthesize templateEditorToggleButton;
@synthesize textElementInfoView;
@synthesize ratingElementInfoView;
@synthesize dateElementInfoView;
@synthesize infoViewDictionary;
@synthesize entryTemplateSelectionCountIsOne;
@synthesize templateEditorMessageString;


#pragma mark -
#pragma mark Initialization

-(void)awakeFromNib {
	// The template editor column view's resizing mask is being reset from the value set in the nib, but I can't figure out where it's happening.  This just resets it to the proper value
	[[self templateEditorElementsColumnView] setAutoresizingMask:NSViewWidthSizable | NSViewMaxYMargin];
//	NSString *sortKey = [NSString stringWithFormat:@"%@.%@", ALRepresentedObjectKey, ALLineNumberKey];
	NSSortDescriptor *lineNumberSortDescriptor = [[NSSortDescriptor alloc] initWithKey:ALLineNumberKey ascending:YES];
	[[self templateEditorElementsColumnView] setChildEntriesSortDescriptors:[NSArray arrayWithObject:lineNumberSortDescriptor]];
	// Observing selected entries so we can retract the info view when the selection changes
	[[self templateEditorElementsColumnView] addObserver:self forKeyPath:@"selectedEntries" options:NSKeyValueObservingOptionNew context:NULL];
	
	[[self templateLibraryPopover] setPreferredContentSize:[self defaultContentSize_]];
	[[self templateEditorElementsColumnView] addObserver:self forKeyPath:@"childEntries" options:NSKeyValueObservingOptionNew context:NULL];
	[[self templateEditorDocumentView] addSubview:[self templateEditorElementsColumnView]];
	[[self templateEditorDocumentView] addSubview:[self templateEditorStandardFieldsView]];
	[self tileTemplateEditorView_];
	
	NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
	// Watch template editor column view's frame to re-tile the editor views
	[noteCenter addObserver:self selector:@selector(templateEditorElementsColumnViewDidChangeFrame:) name:NSViewFrameDidChangeNotification object:[self templateEditorElementsColumnView]];
	// Watch the popover content view's frame for saving in user defaults
	[noteCenter addObserver:self selector:@selector(templateLibraryPopoverContentViewDidChangeFrame:) name:NSViewFrameDidChangeNotification object:[[self templateLibraryPopover] contentView]];
	// This notifies us that entries were added using the 'add' button, so we can dismiss the popup
	[noteCenter addObserver:self selector:@selector(entriesWereAdded:) name:ALEntriesWereAddedNotification object:nil];
	
	[self setInfoViewDictionary:[NSDictionary dictionaryWithObjectsAndKeys:[self textElementInfoView], [NSNumber numberWithInteger:ALElementTypeText], nil]];
	
	[self updateTemplateEditorKeyViewLoop_];
	NSString *collapsedKeyPath = [NSString stringWithFormat:@"values.%@", ALTemplateEditorIsCollapsedKey];
	[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:collapsedKeyPath options:NSKeyValueObservingOptionNew context:NULL];
	
	[[self window] addObserver:self forKeyPath:@"firstResponder" options:NSKeyValueObservingOptionNew context:NULL];
}


#pragma mark -
#pragma mark Accessors

-(SMRPopoverWindow *)templateLibraryPopover {
	return (SMRPopoverWindow *)[self window];
}

-(NSArray *)templateLibrarySortDescriptors {
	return [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:ALLabelKey ascending:YES]];
}


#pragma mark -
#pragma mark Notifications

-(void)templateEditorElementsColumnViewDidChangeFrame:(NSNotification *)note {
	[self tileTemplateEditorView_];
}

-(void)templateLibraryPopoverContentViewDidChangeFrame:(NSNotification *)note {
	NSSize contentSize = [[[self templateLibraryPopover] contentView] frame].size;
	[self setDefaultContentSize_:contentSize];
}

-(void)entriesWereAdded:(NSNotification *)note {
	NSWindow *mainWindow = [[self templateLibraryPopover] parentWindow];
	[mainWindow removeChildWindow:[self templateLibraryPopover]];
	[[self templateLibraryPopover] orderOut:self];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
	if ([keyPath isEqualToString:@"firstResponder"]) {
		NSView *nextKeyView = [[[self window] firstResponder] isKindOfClass:[NSView class]] ? [(NSView *)[[self window] firstResponder] nextKeyView] : nil;
		NSLog(@"firstResponder: %@  next: %@", [[[self window] firstResponder] className], [nextKeyView className]);
		return;
	}
	
	// Observing selectedEntries and childEntries properties
	if (object == [self templateEditorElementsColumnView]) {
		NSArray *allElementEntries = [[self templateEditorElementsColumnView] childEntries];
		[allElementEntries makeObjectsPerformSelector:@selector(hideInfoView)];
		return;
	}
	// Observing sharedUserDefaultsController with key ALTemplateEditorIsCollapsedKey
	[self updateTemplateEditorKeyViewLoop_];
}


#pragma mark -
#pragma mark Window delegate

-(NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
    return [[self managedObjectContext] undoManager];
}

-(void)keyDown:(NSEvent *)theEvent {
	[self interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
}

-(void)deleteBackward:(id)sender {
	// Handles only deletion from the template editor's elements column view
	NSResponder *firstResponder = [[self templateLibraryPopover] firstResponder];
	if (![firstResponder isKindOfClass:[NSView class]] || ![(NSView *)firstResponder isDescendantOf:[self templateEditorElementsColumnView]]) {
		[super deleteBackward:sender];
		return;
	}
	
	// Get selected objects from the column view
	[[self managedObjectContext] processPendingChanges];
	NSArray *selectedTemplates = [[self entryTemplatesArrayController] selectedObjects];
	if ([selectedTemplates count] != 1) {
		NSLog(@"ALTemplateLibraryPopoverController: trying to delete with selected templates != 1");
		NSBeep();
		return;
	}
	ALEntryTemplate *selectedEntryTemplate = [selectedTemplates objectAtIndex:0];
	NSArray *selectedObjects = [[self templateEditorElementsColumnView] selectedObjects];
	if (!selectedObjects || [selectedObjects count] == 0) {
		NSBeep();
		return;
	}
	// Delete selected and reset line numbers
	[[self managedObjectContext] smrDeleteObjects:selectedObjects];
	NSSortDescriptor *lineNumberSortDescriptor = [[NSSortDescriptor alloc] initWithKey:ALLineNumberKey ascending:YES];
	NSArray *sortedElements = [[selectedEntryTemplate elementTemplates] sortedArrayUsingDescriptors:[NSArray arrayWithObject:lineNumberSortDescriptor]];
	short lineNumber = 0;
	for (ALElementTemplate *elementTemplate in sortedElements) 
		[elementTemplate setLineNumber:[NSNumber numberWithShort:lineNumber++]];
}

- (void)cancelOperation:(id)sender {
	NSWindow *parentWindow = [[self window] parentWindow];
	[parentWindow removeChildWindow:[self window]];
	[[self window] orderOut:self];
}


#pragma mark -
#pragma mark Library table view delegate

-(void)tableViewSelectionDidChange:(NSNotification *)aNotification {
	NSUInteger selectionCount = [[[self entryTemplatesArrayController] selectedObjects] count];
	[self setEntryTemplateSelectionCountIsOne:selectionCount == 1];
	// TODO: MAKE THESE STRINGS CONSTANTS
	if (selectionCount > 1)
		[self setTemplateEditorMessageString:@"Multiple entries selected"];
	else if (selectionCount == 0)
		[self setTemplateEditorMessageString:@"No selection"];
}

-(CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
	NSTableColumn *tableColumn = [[tableView tableColumns] objectAtIndex:0];
	NSCell *tableCell = [tableView preparedCellAtColumn:0 row:row];
	NSRect cellBounds = NSMakeRect(0, 0, [tableColumn width], CGFLOAT_MAX);
	NSSize minSize = [tableCell cellSizeForBounds:cellBounds];
	return minSize.height + ALTemplateLibraryTableCellVerticalPadding;
}


#pragma mark -
#pragma mark Template element column view delegate

-(NSView *)columnView:(SMRColumnView *)aColumnView willForwardHitTestAtPoint:(NSPoint)aPoint toSubview:(NSView *)subview {
	if ([subview isKindOfClass:[NSButton class]])
		return subview;
	return aColumnView;
}


#pragma mark -
#pragma mark Template library methods

-(void)displayTemplateLibraryPopoverFromView:(NSView *)targetView {
	[[self templateLibraryPopover] presentPopoverFromRect:[targetView bounds] inView:targetView permittedArrowDirections:SMRPopoverArrowDirectionUp | SMRPopoverArrowDirectionDown animated:YES];
	[self tileTemplateEditorView_];
}

-(IBAction)insertElementTemplate:(id)sender {
	// Get which element was clicked
	SMRColumnEntry *clickedEntry = [[self templateEditorElementsColumnView] entryWithView:sender];
	if (!clickedEntry)
		return;
	ALElementTemplate *clickedElement = [clickedEntry representedObject];
	short clickedLineNumber = [[clickedElement lineNumber] shortValue];
	
	NSUndoManager *undoManager = [[self managedObjectContext] undoManager];
	[undoManager beginUndoGrouping];
	[undoManager setActionName:@"Insert Element"];
	
	// Adjust line numbers for existing elements to make room for the new element
	SMRColumnEntry *clickedParent = [clickedEntry parentEntry];
	NSArray *clickedSiblings = [clickedParent childEntries];
	for (ALElementTemplate *siblingElement in [clickedSiblings valueForKey:ALRepresentedObjectKey]) {
		short lineNumber = [[siblingElement lineNumber] shortValue];
		if (lineNumber <= clickedLineNumber)
			continue;
		[siblingElement setLineNumber:[NSNumber numberWithShort:lineNumber + 1]];
	}
	
	// Insert the new element into the template
	ALElementTemplate *newElementTemplate = [ALElementTemplate insertInManagedObjectContext:[self managedObjectContext]];
	[newElementTemplate setLineNumber:[NSNumber numberWithShort:clickedLineNumber + 1]];
	
	// Propogate new element through all existing entries
	ALEntryTemplate *entryTemplate = [clickedElement entryTemplate];
	[newElementTemplate setEntryTemplate:entryTemplate];
	for (ALEntry *entry in [entryTemplate entries]) {
		ALElement *newElement = [newElementTemplate stamp];
		[newElement setEntry:entry];
	}
	[undoManager endUndoGrouping];
}

-(IBAction)toggleElementInfoView:(id)sender {
	ALTemplateEditorElementColumnEntry *toggledColumnEntry = (ALTemplateEditorElementColumnEntry *) [[self templateEditorElementsColumnView] entryWithView:sender];
	if (!toggledColumnEntry)
		return;
	if ([toggledColumnEntry isDisplayingInfoView]) {
		[toggledColumnEntry hideInfoView];
		return;
	}
	NSArray *siblings = [toggledColumnEntry siblingEntriesIncludingSelf:NO];
	[siblings makeObjectsPerformSelector:@selector(hideInfoView)];
	[[self templateEditorElementsColumnView] selectEntry:toggledColumnEntry extendSelection:NO];
	ALElementTemplate *representedObject = [toggledColumnEntry representedObject];
	NSView *infoView = [[self infoViewDictionary] objectForKey:[representedObject elementType]];
	if (!infoView)
		NSLog(@"Didn't find an infoView for %@", representedObject);
	[toggledColumnEntry displayInfoView:infoView];
}

-(void)tileTemplateEditorView_ {
	CGFloat columnViewHeight = NSHeight([[self templateEditorElementsColumnView] frame]);
	CGFloat standardFieldsHeight = NSHeight([[self templateEditorStandardFieldsView] frame]);
	CGFloat documentHeight = (ALDetailToolbarTemplateEditorVerticalPadding * 2) + columnViewHeight + standardFieldsHeight + ALDetailToolbarTemplateEditorGroupPadding;
	NSRect editorFrame = [[self templateEditorDocumentView] bounds];
	NSSize documentSize = NSMakeSize(NSWidth(editorFrame), documentHeight);
	[[self templateEditorDocumentView] setFrameSize:documentSize];
	
	editorFrame = NSInsetRect(editorFrame, ALDetailToolbarTemplateEditorHorizontalPadding, ALDetailToolbarTemplateEditorVerticalPadding);
	editorFrame.size.height = [[self templateEditorElementsColumnView] frame].size.height;
	[[self templateEditorElementsColumnView] setPostsFrameChangedNotifications:NO];
	[[self templateEditorElementsColumnView] setFrame:editorFrame];
	[[self templateEditorElementsColumnView] setPostsFrameChangedNotifications:YES];
	
	editorFrame.origin.y += editorFrame.size.height + ALDetailToolbarTemplateEditorGroupPadding;
	editorFrame.size.height = [[self templateEditorStandardFieldsView] frame].size.height;
	[[self templateEditorStandardFieldsView] setFrame:editorFrame];
}

-(NSSize)defaultContentSize_ {
	NSString *contentSizeString = [[NSUserDefaults standardUserDefaults] stringForKey:ALDetailToolbarTemplateLibraryPreferredContentSize];
	if (!contentSizeString)
		return ALDetailToolbarTemplateLibraryPopoverDefaultSize;
	return NSSizeFromString(contentSizeString);
}

-(void)setDefaultContentSize_:(NSSize)contentSize {
	[[NSUserDefaults standardUserDefaults] setObject:NSStringFromSize(contentSize) forKey:ALDetailToolbarTemplateLibraryPreferredContentSize];
}

-(void)updateTemplateEditorKeyViewLoop_ {
	BOOL editorIsCollapsed = [[NSUserDefaults standardUserDefaults] boolForKey:ALTemplateEditorIsCollapsedKey];
	NSView *nextView = editorIsCollapsed ? (NSView *)[self templateEditorToggleButton] : (NSView *)[self templateEditorElementsColumnView];
	[[self templateLibraryTableView] setNextKeyView:nextView];
}

@end
